package receiver;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author Marvin Effing & Nick Velthorst
 * @version 0.3
 * 
 * CODE	Meaning
 * 200	(nonstandard success response, see rfc876)
 * 211	System status, or system help reply
 * 214	Help message
 * 220!	<domain> Service ready
 * 221!	<domain> Service closing transmission channel
 * 250!	Requested mail action okay, completed
 * 251	User not local; will forward to <forward-path>
 * 354!	Start mail input; end with <CRLF>.<CRLF>
 * 421!	<domain> Service not available, closing transmission channel
 * 450	Requested mail action not taken: mailbox unavailable
 * 451	Requested action aborted: local error in processing
 * 452	Requested action not taken: insufficient system storage
 * 500!	Syntax error, command unrecognised
 * 501!	Syntax error in parameters or arguments
 * 502	Command not implemented
 * 503	Bad sequence of commands
 * 504	Command parameter not implemented
 * 521	<domain> does not accept mail (see rfc1846)
 * 530	Access denied (???a Sendmailism)
 * 550	Requested action not taken: mailbox unavailable
 * 551	User not local; please try <forward-path>
 * 552	Requested mail action aborted: exceeded storage allocation
 * 553	Requested action not taken: mailbox name not allowed
 * 554	Transaction failed
 * 
 * Codes are supplemented with '!' which will be implemented
 */
public class Receiver {

	//commands
	private static String  HELO	 	= "HELO";
	private static String  MAIL	 	= "MAIL";
	private static String  RCPT	 	= "RCPT";
	private static String  DATA	 	= "DATA";
	private static String  POINT 	= ".";
	private static String  QUIT	    = "QUIT";
	
	private static boolean dataBool = false;

	//status codes to be put in this map
	private static Map<Integer, String> STATUSCODE;
	private static ArrayList<String> COMMANDS;
	private static InetAddress client;
	

	@SuppressWarnings("unchecked")
	public static void main ( String args[] ) throws Exception {
		initialize();
		
		String inMessage, outMessage;
		ServerSocket welcomeSocket = new ServerSocket(6789); //customSMTP poort

		ArrayList<String> order = new ArrayList<String>();
		order = (ArrayList<String>) COMMANDS.clone();

		while (true) {

			Socket connectionSocket = welcomeSocket.accept();
			BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
			DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
			inMessage = inFromClient.readLine();
			outMessage = inMessage;

			//start with the command input
			//check validity of the command
			String[] tempArray = inMessage.split(" ");
			String cmd = tempArray[0]; //first input should always be a command

			String inet = tempArray[tempArray.length-1]; //last index of array is always automatically inetaddress (local)
			
			boolean validCommand = checkCommand(cmd); //temp is true/false depending on the validity of the cmd
			boolean endSession = false;
			
			if (!validCommand && !dataBool) {
				int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Syntax error, command unrecognised");
				outMessage = statuscode +" Syntax error, command unrecognised";

			}
			else {
				if(!dataBool) {
					//order of commands:
					//1. HELO, 2. MAIL, 3. RCPT, 4. DATA, 5. '.', 6. QUIT
					if(cmd.equals(order.get(0))) {
						if (cmd.equalsIgnoreCase(HELO)) {
							int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Requested mail action okay, completed");
							outMessage = statuscode + " Hello " + inet + ", pleased to meet you";
							order.remove(0); //remove HELO, since it wont be used again
						} else if (cmd.equalsIgnoreCase(MAIL)) { 
							int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Requested mail action okay, completed");
							outMessage = statuscode + " Requested mail action okay, completed";
							order.remove(0); //remove MAIL, since it wont be used again
						} else if (cmd.equalsIgnoreCase(RCPT)) {
							int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Requested mail action okay, completed");
							outMessage = statuscode + " Requested mail action okay, completed";
							order.remove(0); //remove RCPT, since it wont be used again
						} else if (cmd.equalsIgnoreCase(DATA)) {
							int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Start mail input; end with <CRLF>.<CRLF>");
							outMessage = statuscode + " Start mail input; end with '.'";
							order.remove(0); //remove DATA, since it wont be used again
							dataBool = true;
						}  else if (cmd.equalsIgnoreCase(QUIT)) {
							int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Service closing transmission channel");
							outMessage = statuscode + " " + inet + " Service closing transmission channel";
							endSession = true;

						}
					} else {
						int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Bad sequence of commands");
						outMessage = statuscode + " Bad sequence of commands";
					}

				} else {
					//is data, check op POINT
					if (cmd.equalsIgnoreCase(POINT)) {
						dataBool = false;
						int statuscode = (Integer) getKeyFromValuesSingleObject(STATUSCODE, "Requested mail action okay, completed");
						outMessage = statuscode + " OK, end of DATA";
						order.remove(0);
					}
				}
			}

			System.out.println("INCOMING MESSAGE: " + inMessage);
			System.out.println("OUTGOING MESSAGE: " + outMessage);
			System.out.println("--------------");

			outToClient.writeBytes(outMessage + "\n");
			
			if (endSession) {
				connectionSocket.close();
				System.out.println("Connection closed");
				System.exit(0);
			}
		}
	}

	public static void initialize() {
		COMMANDS = new ArrayList<String>();

		COMMANDS.add(HELO);
		COMMANDS.add(MAIL);
		COMMANDS.add(RCPT);
		COMMANDS.add(DATA);
		COMMANDS.add(POINT);
		COMMANDS.add(QUIT);

		STATUSCODE = new HashMap<Integer, String>();
		STATUSCODE.put(220, "Service ready");
		STATUSCODE.put(221, "Service closing transmission channel");
		STATUSCODE.put(250, "Requested mail action okay, completed");
		STATUSCODE.put(354, "Start mail input; end with <CRLF>.<CRLF>");
		STATUSCODE.put(421, "Service not available, closing transmission channel");
		STATUSCODE.put(500, "Syntax error, command unrecognised");
		STATUSCODE.put(503, "Bad sequence of commands");
	}



	public static Object getKeyFromValuesSingleObject(Map<?,?> hm, Object value) {
		Object ret = null;
		for(Object o:hm.keySet()){
			if(hm.get(o).equals(value)) {
				ret = o;
			}
		}
		return ret;
	}

	public static boolean checkCommand(String cmd) {
		if (!COMMANDS.contains(cmd)) {
			System.out.println("--------------");
			System.out.println("COMMAND IS INVALID");
			return false;
		} else {
			System.out.println("--------------");
			System.out.println("COMMAND IS VALID");
			return true;
		}
	}

	public static InetAddress getInetClient() {
		return client;
	}

	public static void setInetClient(InetAddress ia) {
		client = ia;
	}
}
